package nanzhuli.user.svc.service.impl;

import com.fasterxml.jackson.core.type.TypeReference;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import nanzhuli.user.svc.mapper.DayRestMapper;
import nanzhuli.user.svc.mapper.RestLogMapper;
import nanzhuli.user.svc.mapper.StaffBonusMapper;
import nanzhuli.user.svc.mapper.StaffRestMapper;
import nanzhuli.user.svc.model.entity.DayRest;
import nanzhuli.user.svc.model.entity.RestLog;
import nanzhuli.user.svc.model.entity.StaffBonus;
import nanzhuli.user.svc.model.entity.StaffRest;
import nanzhuli.user.svc.model.resp.excel.OriginalRecord;
import nanzhuli.user.svc.model.resp.ozi.rest.Holiday;
import nanzhuli.user.svc.model.resp.ozi.rest.HolidayResp;
import nanzhuli.user.svc.service.FileService;
import nanzhuli.user.svc.util.JsonUtil;
import nanzhuli.user.svc.util.RestTempClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
@Slf4j
@RequiredArgsConstructor
public class FileServiceImpl implements FileService {

    private static final BigDecimal DAILYMEALSUBSIDY = new BigDecimal("30");
    private static final SimpleDateFormat DATEDF = new SimpleDateFormat("yyyy-MM-dd HH:mm");
    private static final SimpleDateFormat TIMEDF = new SimpleDateFormat("HH:mm");
    private static final SimpleDateFormat DAYDF = new SimpleDateFormat("yyyy-MM-dd");

    @Value("#{'${need.cal.rest.departments}'.split(',')}")
    private List<String> needCalRestDepartments;
    @Resource
    final RestTempClient restTempClient;
    @Resource
    final RedisTemplate<String, String> redisTemplate;
    @Resource
    DayRestMapper dayRestMapper;
    @Resource
    StaffRestMapper staffRestMapper;
    @Resource
    RestLogMapper restLogMapper;

    @Resource
    StaffBonusMapper staffBonusMapper;

    /**
     * 解析excel计算员工调休
     * @param allOriginalRecords 原始数据
     * 工作日：晚上加班时间 - 早上迟到时间）/ 2
     * 周六、日：晚上加班时间 - 早上迟到时间）
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void calRest(List<OriginalRecord> allOriginalRecords) throws Exception {
        final long morningTime = TIMEDF.parse("08:30").getTime();
        final long nightTime = TIMEDF.parse("18:30").getTime();

        // 所有员工调休表
        List<DayRest> dayRests = new ArrayList<>();
        List<RestLog> restLogs = new ArrayList<>();
        // 做一个过滤，只计算研发部的调休 1201由于组织架构调整，现在改为计算所有
        Map<String, List<OriginalRecord>> staffOriginalRecordMap = allOriginalRecords.stream()
                .collect(Collectors.groupingBy(OriginalRecord::getName));
        // 计算每个员工的调休时长
        Map<String, List<DayRest>> staffMap = new HashMap<>();
        Date date = new Date();
        for (String name : staffOriginalRecordMap.keySet()){
            // 每个员工调休表
            List<DayRest> staffDayRests = new ArrayList<>();
            List<OriginalRecord> staffOriginalRecords = staffOriginalRecordMap.get(name);

            if(null != staffOriginalRecords && !staffOriginalRecords.isEmpty()){
                OriginalRecord originalRecord = staffOriginalRecords.get(0);
                Map<String, List<OriginalRecord>> attendanceDateMap = staffOriginalRecords.stream().collect(Collectors.groupingBy(OriginalRecord::getAttendanceDate));
                // attendanceDateStr格式：20-09-01 星期二
                for (String attendanceDateStr : attendanceDateMap.keySet()) {
                    List<OriginalRecord> attendOriginalRecords = attendanceDateMap.get(attendanceDateStr);
                    // 过滤考勤时间为空的数据
                    attendOriginalRecords = attendOriginalRecords.stream().filter(e -> !StringUtils.isEmpty(e.getAttendanceTime())).collect(Collectors.toList());
                    // 考勤日数据少于两条，跳过不计算调休
                    if (attendOriginalRecords.size() < 2) {
                        continue;
                    }
                    // 判断是否为工作日（非工作日所有上班时间均算为加班）
                    boolean isWorkingDay = isWorkingDay(attendanceDateStr);
                    // 考勤日数据大于等于两条（上午和下午的打卡时间均有则不跳过）
                    if (!hasRest(attendOriginalRecords, isWorkingDay)) {
                        continue;
                    }

                    // 格式：2020-09-01
                    Date attendanceDate = DAYDF.parse("20" + attendanceDateStr.substring(0, 8));
                    // 取第一条记录
                    OriginalRecord startOriginalRecord = attendOriginalRecords.get(0);
                    // 取最后一条记录
                    OriginalRecord endOriginalRecord = attendOriginalRecords.get(attendOriginalRecords.size() - 1);

                    // 计算单日调休时长
                    long restTime;
                    long overMinutes = 0;
                    long lateMinutes = 0;
                    if(isWorkingDay){
                        // 获取晚上加班时间
                        Date nightCheckInTime = DATEDF.parse(endOriginalRecord.getCheckInTime());
                        long offWorkTime = DATEDF.parse(DATEDF.format(nightCheckInTime)).getTime();
                        String attendanceTime = endOriginalRecord.getAttendanceTime();
                        // 加班分钟数 -> 打卡时间 - 考勤时间
                        if (!StringUtils.isEmpty(attendanceTime)) {
                            overMinutes = (offWorkTime - DATEDF.parse(attendanceTime).getTime() - (1000 * 60 * 30) < 0 ) ? 0 : (offWorkTime - DATEDF.parse(attendanceTime).getTime() - (1000 * 60 * 30)) / (1000 * 60);
                        }

                    //    overMinutes = (offWorkTime - nightTime) < 0 ? 0 : (offWorkTime - nightTime) / (1000 * 60);
                        // 获取早上迟到时间
                        Date morningCheckInTime = DATEDF.parse(startOriginalRecord.getCheckInTime());
                        long workTime = DATEDF.parse(DATEDF.format(morningCheckInTime)).getTime();
                        attendanceTime = startOriginalRecord.getAttendanceTime();
                        // 迟到分钟数 -> 打卡时间 - 考勤时间
                        if (!StringUtils.isEmpty(attendanceTime)) {
                            lateMinutes = (workTime - DATEDF.parse(attendanceTime).getTime()) < 0 ? 0 : (workTime - DATEDF.parse(attendanceTime).getTime()) / (1000 * 60);
                        }
                     //   lateMinutes = (workTime - morningTime) < 0 ? 0 : (workTime - morningTime) / (1000 * 60);
                        // 可调休时间
                        restTime = overMinutes - lateMinutes;
                        restTime = restTime > 0 ? restTime / 2 : 0;
                    }else {
                        // 最后一条
                        Date lastCheckInTime = DATEDF.parse(endOriginalRecord.getCheckInTime());
                        long lastCheckInMin = lastCheckInTime.getTime();

                        // 第一条
                        Date firstCheckInTime = DATEDF.parse(startOriginalRecord.getCheckInTime());
                        long firstCheckInMin = firstCheckInTime.getTime();

                        restTime = lastCheckInMin - firstCheckInMin;
                        restTime = restTime > 0 ? restTime / (1000 * 60) : 0;
                    }

                    // 第二次导入，数据库name+attendance_date的唯一联合索引直接报错
                    DayRest dayRest = new DayRest();
                    dayRest.setName(name);
                    dayRest.setAttendanceGroup(originalRecord.getAttendanceGroup());
                    dayRest.setDepartment(originalRecord.getDepartment());
                    dayRest.setJobNum(originalRecord.getJobNum());
                    dayRest.setPosition(originalRecord.getPosition());
                    dayRest.setAttendanceDate(attendanceDate);
                    dayRest.setWeek(attendanceDateStr.substring(9, attendanceDateStr.length()));
                    dayRest.setIsWorkDay(isWorkingDay ? "2" : "1");
                    if(isWorkingDay){
                        dayRest.setLateTime((int)lateMinutes);
                        dayRest.setOverTime((int)overMinutes);
                    }else {
                        dayRest.setLateTime(0);
                        dayRest.setOverTime((int)restTime);
                    }
                    dayRest.setRestTime((int)restTime);
                    dayRest.setRestDuration(getDurationStr(restTime));
                    dayRest.setCreateTime(date);
                    dayRests.add(dayRest);
                    staffDayRests.add(dayRest);


                    RestLog restLog = new RestLog();
                    restLog.setName(name);
                    restLog.setJobNum(originalRecord.getJobNum());
                    restLog.setOperateType("1");
                    restLog.setOperateData((int)restTime);
                    restLog.setCreateUser("");
                    restLog.setCreateTime(date);
                    restLogs.add(restLog);
                }
                staffMap.put(name, staffDayRests);
            }
        }
        // 记录每日调休表
        if(!dayRests.isEmpty()){
            dayRestMapper.insertList(dayRests);
        }

        // 记录累计调休表
        Set<String> staffNames = staffMap.keySet();
        List<StaffRest> staffRests = staffRestMapper.selectByNames(new ArrayList<>(staffNames));
        Map<String, StaffRest> staffRestMap = staffRests.stream().collect(Collectors.toMap(StaffRest::getName, e -> e));
        for (String name : staffMap.keySet()){
            List<DayRest> staffDayRests = staffMap.get(name);
            if(null != staffDayRests && !staffDayRests.isEmpty()){
                // 某人总加班时长
                int overTime = staffDayRests.stream().mapToInt(DayRest::getOverTime).sum();
                // 某人总调休时长
                int restTime = staffDayRests.stream().mapToInt(DayRest::getRestTime).sum();
                StaffRest staffRest = staffRestMap.get(name);
                if(null == staffRest){
                    DayRest dayRest = staffDayRests.get(0);
                    staffRest = new StaffRest();
                    staffRest.setName(name);
                    staffRest.setJobNum(dayRest.getJobNum());
                    staffRest.setTotalOverTime(overTime);
                    staffRest.setTotalRestTime(0);
                    staffRest.setLastRestTime(restTime);
                    staffRest.setLastRestDuration(getDurationStr(restTime));
                    staffRest.setUpdateTime(date);
                    staffRestMapper.insert(staffRest);
                }else {
                    staffRest.setTotalOverTime(staffRest.getTotalOverTime() + overTime);
                    staffRest.setTotalRestTime(staffRest.getTotalRestTime());
                    staffRest.setLastRestTime(staffRest.getLastRestTime() + restTime);
                    staffRest.setLastRestDuration(getDurationStr(staffRest.getLastRestTime()));
                    staffRest.setUpdateTime(date);
                    staffRestMapper.updateByPrimaryKeySelective(staffRest);
                }
            }
        }

        // 记录日志表
        if(!restLogs.isEmpty()){
            restLogMapper.insertList(restLogs);
        }
    }

    @Override
    public void saveStaffBouns(List<StaffBonus> staffBonusList) throws Exception {
        int count = staffBonusMapper.saveStaffBonus(staffBonusList);
        log.info("count={}",count);
    }

    /**
     * 判断有否早上和下午的打卡时间均有
     * @param attendOriginalRecords 个人某考勤日的所有打卡记录
     * @return true：有，false：无
     */
    private boolean hasRest(List<OriginalRecord> attendOriginalRecords, boolean isWorkingDay) throws ParseException {
        // 非工作日
        if(!isWorkingDay){
            return true;
        // 工作日
        }else {
            boolean hasMorningCheckTime = false;
            boolean hasAfterCheckTime = false;
            if(null != attendOriginalRecords && attendOriginalRecords.size() > 1){
                OriginalRecord firstOriginalRecord = attendOriginalRecords.get(0);
                // FIXME 工作日暂以13:30为分界线区分上午和下午，通过考勤时间获取当日13:30的时刻
                // FIXME 每日19:30后的打卡才计算调休
                Calendar midTimeCalendar = Calendar.getInstance();
                midTimeCalendar.setTime(DATEDF.parse(firstOriginalRecord.getAttendanceTime()));
                midTimeCalendar.set(Calendar.HOUR, 13);
                midTimeCalendar.set(Calendar.MINUTE, 30);
                Date midTime = midTimeCalendar.getTime();

                Calendar overtimeCalendar = Calendar.getInstance();
                overtimeCalendar.setTime(DATEDF.parse(firstOriginalRecord.getAttendanceTime()));
                overtimeCalendar.set(Calendar.HOUR, 19);
                overtimeCalendar.set(Calendar.MINUTE, 30);
                Date earliestOvertime = overtimeCalendar.getTime();

                for (OriginalRecord originalRecord : attendOriginalRecords){
                    // 获取打卡时间
                    String checkInTime = originalRecord.getCheckInTime();
                    Date checkTime = DATEDF.parse(checkInTime);
                    if(checkTime.before(midTime)){
                        hasMorningCheckTime = true;
                    }
                    if(checkTime.after(earliestOvertime)){
                        hasAfterCheckTime = true;
                    }
                }
                return hasMorningCheckTime && hasAfterCheckTime;
            }
            return false;
        }
    }

    /**
     * 分钟数转换为 -> 小时 + 分钟
     * @param minutes 分钟数
     * @return 小时 + 分钟
     */
    private String getDurationStr(long minutes){
        return (minutes / 60) + "小时" + (minutes % 60) + "分钟";
    }

    /**
     * 是否为周末
     * @param date 日期
     * @return true：是，false：否
     */
    private boolean isWeekend(Date date){
        // TODO 添加校验
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        if (calendar.get(Calendar.DAY_OF_WEEK) == 1) {
            return true;
        } else {
            return 6 == calendar.get(Calendar.DAY_OF_WEEK) - 1;
        }
    }

    /**
     * 判断是否为工作日
     * @param attendanceDate 考勤日期
     * @return true：工作日，false：周末/假期
     */
    public boolean isWorkingDay(String attendanceDate) throws Exception {
        // 2020-10-10 10:10:10
        // 20-09-01 星期二
        String day = "20" + attendanceDate.substring(0, 8);
        String month = "20" + attendanceDate.substring(0, 5);

        // 从缓存中获取
        String holidayJson = redisTemplate.opsForValue().get("rest:" + month);
        if(StringUtils.isEmpty(holidayJson)){
            holidayJson = restTempClient.getHolidayInfos(month);
            // 远程调用的结果存入缓存
            redisTemplate.opsForValue().set("rest:" + month, holidayJson, 30, TimeUnit.DAYS);
        }

        List<HolidayResp> holidayResps = JsonUtil.json2Object(holidayJson, new TypeReference<List<HolidayResp>>() {
        });
        if(null == holidayResps || holidayResps.isEmpty()){
            log.error("通过万年历api获取假期数据失败");
            throw new Exception("通过万年历api获取假期数据失败");
        }
        Set<Holiday> holidays = holidayResps.stream().flatMap(e -> e.getList().stream()).collect(Collectors.toSet());
        Map<String, String> dateMap = holidays.stream().collect(Collectors.toMap(e -> DAYDF.format(e.getDate()), Holiday::getStatus));
        String status = dateMap.get(day);
        if(!StringUtils.isEmpty(status)){
            if("1".equals(status)){
                return false;
            }else if("2".equals(status)){
                return true;
            }
        }
        return !isWeekend(DAYDF.parse(day));
    }

}
